//build: 20240524
//The camera image capture component is mainly used to acquire QR code images and then use the dxDecoder component to parse the QR code images.
import { capturerClass } from './libvbar-m-dxcapturer.so'
import std from './dxStd.js'
import dxMap from './dxMap.js'
import dxCommon from './dxCommon.js';
import bus from './dxEventBus.js'
const capturerObj = new capturerClass();
const map = dxMap.get('default')
const capturer = {}

/**
 * Image retrieval module initialization
 * @param {object} options Most configuration parameters can be set to default values.
 * @param {string} options.path                 Required: The path to the image acquisition device. This path varies depending on the device
 * @param {number} options.width                Optional field: Image width (default is 0).
 * @param {number} options.height               Optional field, image height, default is 0.
 * @param {number} options.widthbytes           Optional field. Number of bytes per pixel: GREY: 1, YUV: 2
 * @param {number} options.pixel_format          Optional, pixel format, default is 1497715271 representing V4L2_PIX_FMT_GREY
 * @param {number} options.max_channels          Optional. Maximum supported number of synchronous output channels; default is 3.
 * @param {number} options.rotation             Optional field: Rotation angle. The default value is 90 degrees.
 * @param {number} options.frame_num             Optional field, frame number, default is 3
 * @param {number} options.preview_enable        Optional field; preview enabled or disabled. Default is 3.
 * @param {number} options.preview_left          Optional field: Coordinates of the left border of the preview box; default is 0.
 * @param {number} options.preview_top           Optional field: Coordinates of the top border of the preview box; default is 0.
 * @param {number} options.preview_width         Optional field: Preview frame width. 
 * @param {number} options.preview_height        Optional field, preview box height
 * @param {number} options.preview_rotation      Optional field: Preview box rotation angle; the default value is 0.
 * @param {number} options.preview_mode          Optional, preview box mode, default is 2
 * @param {number} options.preview_screen_index   Optional field; preview box index; default is 0.
 * @param {string} id Handle ID, optional (a unique ID is required if initializing multiple instances).
 */
capturer.init = function (options, id) {
    if (options.path === undefined || options.path === null || options.path.length < 1) {
        throw new Error("dxCapturer.init: 'path' parameter should not be null or empty")
    }
    let pointer = capturerObj.init(options);
    if (!pointer) {
        throw new Error("dxCapturer.init: init failed")
    }
    dxCommon.handleId("capturer", id, pointer)
}

/**
 * Callback Registration
 * @param {string} id Handle ID, optional (must be consistent with the ID in init).
 * @param {string} capturerDogId Camera watchdog handle ID (optional)
 * @returns true/false
 */
capturer.registerCallback = function (id, capturerDogId) {
    let pointer = dxCommon.handleId("capturer", id)
    let capturerDogPointer = null;
    print("capturerDogPointer:", capturerDogPointer)
    if(capturerDogId){
        capturerDogPointer = dxCommon.handleId("watchdog", capturerDogId)
        print("capturerDogPointer:", capturerDogPointer)
    }
    return capturerObj.registerCallback(pointer, "decoderCapturerImage", capturerDogPointer)
}
/**
 * Get basic information
 * @param {string} id Handle ID, optional (must be consistent with the ID in init).
 * @returns Format like: {"width":800,"widthbytes":1,"height":600,"name":{},"type":6}
 */
capturer.getInfo = function (id) {
    let pointer = dxCommon.handleId("capturer", id)
    return capturerObj.getInfo(pointer)
}
/**
 * Close the image capture module
 * @param {string} id Handle ID, optional (must be consistent with the ID in init).
 * @returns true/false
 */
capturer.close = function (id) {
    let pointer = dxCommon.handleId("capturer", id)
    return capturerObj.close(pointer)
}

/**
 * To retrieve image data, you can call this interface, similar to the `capturer.msgReceive` method. If you use this method, you must manually destroy the retrieved image pointer.
 * @param {string} id Handle ID, optional (must be consistent with the ID in init).
 * @returns image pointer
 */
capturer.readImage = function (id) {
    let pointer = dxCommon.handleId("capturer", id)
    return capturerObj.readImage(pointer)
}

/**
 * Destroy the acquired image pointer, used in conjunction with the `capturer.readImage` method.
 * @param {number} image Image pointer (required)
 * @returns true/false
 */
capturer.destroyImage = function (image) {
    return capturerObj.destroyImage(image)
}

/**
 * Enable/disable capture preview
 * @param {number}  Camera enabled/disabled (required)
 * @param {string} id Handle ID, optional (must be consistent with the ID in init).
 * @returns true/false
 */
capturer.capturerEnable = function (enable, id) {
    if (enable == null) {
        throw new Error("nirEnable should not be null or empty")
    }
    let pointer = dxCommon.handleId("capturer", id)
    return capturerObj.capturerPreviewEnable(pointer, enable)
}

/**
 * @brief Image file to image
 * @param {string} fileName File path
 * @param {number} type Image type: IMAGE_YUV420P = 0, 1; IMAGE_YUV420SP = 1
 * @return imageId image handle id
 */
capturer.pictureFileToImage = function (fileName, type) {
    if (fileName == null) {
        throw new Error("fileName should not be null or empty")
    }
    if (type == null) {
        throw new Error("type should not be null or empty")
    }
    return capturerObj.pictureFileToImage(fileName, type)
}

/**
 * @brief Image cropping
 * @param {string} src_pic Source file path
 * @param {number} width Image width
 * @param {number} height Image height
 * @param {string} out_pic Image path after cropping
 * @return ture/false
 */
capturer.pictureCropping = function (src_pic, width, height, out_pic) {
    if (src_pic == null) {
        throw new Error("src_pic should not be null or empty")
    }
    if (width == null) {
        throw new Error("width should not be null or empty")
    }
    if (height == null) {
        throw new Error("height should not be null or empty")
    }
    if (out_pic == null) {
        throw new Error("out_pic should not be null or empty")
    }
    return capturerObj.pictureCropping(src_pic, width, height, out_pic)
}

/**
 * Image data to image
 * @param {string}  base64Data Image base64 data
 * @param {number}  dataLen data length dataLen
 * @param {number}  type Image type: IMAGE_YUV420P = 0, 1; IMAGE_YUV420SP = 1,
 * @returns imageId image handle id
 */
capturer.pictureDataToImage = function (base64Data, dataLen, type) {
    if (base64Data == null) {
        throw new Error("base64Data should not be null or empty")
    }
    if (dataLen == null) {
        throw new Error("dataLen should not be null or empty")
    }
    if (type == null) {
        throw new Error("type should not be null or empty")
    }
    return capturerObj.pictureDataToImage(base64Data, dataLen, type)
}

// image, (enum image_type)type, (enum vbar_drv_picture_type)save_type, quality, pic_data, data_len
/**
 * image to image data
 * @param {number}  imageId image handle id
 * @param {number}  type Image type: IMAGE_YUV420P = 0, 1; IMAGE_YUV420SP = 1,
 * @param {number}  saveType The converted image types are: TYPE_JPEG = 0, TYPE_BMP = 1, TYPE_PNG = 2, TYPE_UNKNOE = 3;
 * @param {number}  quality Compression ratio: 0-100 for JPEG, not required for lossless PNG compression, and not required for BMP bitmaps.
 * @returns Image base64 data
 */
capturer.imageToPictureData = function (imageId, type, saveType, quality) {
    if (imageId == null) {
        throw new Error("imageId should not be null or empty")
    }
    if (type == null) {
        throw new Error("type should not be null or empty")
    }
    if (saveType == null) {
        throw new Error("saveType should not be null or empty")
    }
    if (quality == null) {
        throw new Error("quality should not be null or empty")
    }
    return capturerObj.imageToPictureData(imageId, type, saveType, quality)
}

/**
 * Convert to image file
 * @param {number}  imageId image handle id
 * @param {string}  type Image type: IMAGE_YUV420P = 0, 1; IMAGE_YUV420SP = 1,
 * @param {number}  saveType The converted image types are: YPE_JPEG = 0, TYPE_BMP = 1, TYPE_PNG = 2, TYPE_UNKNOE = 3;
 * @param {number}  quality Compression ratio: 0-100 for JPEG, not required for lossless PNG compression, and not required for BMP bitmaps.
 * @param {number}  savePath Image save path
 * @returns true/false
 */
capturer.imageToPictureFile = function (imageId, type, saveType, quality, savePath) {
    if (imageId == null) {
        throw new Error("imageId should not be null or empty")
    }
    if (type == null) {
        throw new Error("type should not be null or empty")
    }
    if (saveType == null) {
        throw new Error("saveType should not be null or empty")
    }
    if (quality == null) {
        throw new Error("quality should not be null or empty")
    }
    if (savePath == null) {
        throw new Error("savePath should not be null or empty")
    }
    return capturerObj.imageToPictureFile(imageId, type, saveType, quality, savePath)
}

/**
* Image zoom
* @param {number}   imageId image handle id
* @param {number}   width  target image width
* @param {number}   height Target image height
* @param {number}   mode       Filter mode
*                   FILTER_MODE_NONE     Direct sampling without filtering is the fastest method.
*                   FILTER_MODE_LINEAR   Filtering only along the horizontal direction.
*                   FILTER_MODE_BILINEAR Bilinear filtering; faster than box filtering, but lower quality when reducing image size.
*                   FILTER_MODE_BOX      Box filtering; provides the highest scaling quality.
*/
capturer.imageResizeResolution = function (imageId, width, height, mode) {
    if (imageId == null) {
        throw new Error("imageId should not be null or empty")
    }
    if (width == null) {
        throw new Error("width should not be null or empty")
    }
    if (height == null) {
        throw new Error("height should not be null or empty")
    }
    if (mode == null) {
        throw new Error("mode should not be null or empty")
    }
    return capturerObj.imageResizeResolution(imageId, width, height, mode)
}

/**
 * Check if the message queue of the Controller is empty.
 * @param {string} id Handle ID, optional (must be consistent with the ID in init).
 * @returns true/false
 */
capturer.msgIsEmpty = function (id) {
    let pointer = dxCommon.handleId("capturer", id)
    return capturerObj.msgIsEmpty(pointer)
}

/**
 * Read data from the capturer message queue.
 * @param {string} id Handle ID, optional (must be consistent with the ID in init).
 * @returns image pointer
 */
capturer.msgReceive = function (id) {
    let pointer = dxCommon.handleId("capturer", id)
    return capturerObj.msgReceive(pointer)
}

/**
 * Query the size of the capturer message queue
 * @param {string} id Handle ID, optional (must be consistent with the ID in init).
 * @returns size
 */
capturer.msgQueueSize = function (id) {
    let pointer = dxCommon.handleId("capturer", id)
    return capturerObj.msgQueueSize(pointer)
}

capturer.RECEIVE_MSG = '__capturer__MsgReceive'

/**
 * To simplify the use of the capturer component, the capturer is encapsulated within this worker. Users only need to subscribe to eventbus events to listen to the capturer.
 * @param {object} options For parameters of the capturer component, refer to capturer.init; these are required.
 * @param {string} options.id  Handle ID, optional (a unique ID is required if initializing multiple instances).
 */
capturer.run = function (options) {
    if (options === undefined || options.length === 0) {
        throw new Error("dxcapturer.run:'options' parameter should not be null or empty")
    }
    if (options.id === undefined || options.id === null || typeof options.id !== 'string') {
        // handle id
        options.id = ""
    }
    if (options.path === undefined || options.path === null || options.path.length <= 0) {
        throw new Error("dxcapturer.run:'path' should not be null or empty")
    }
    let oldfilepre = '/app/code/dxmodules/capturerWorker'
    let content = std.loadFile(oldfilepre + '.js').replace("{{id}}", options.id)
    let newfile = oldfilepre + options.id + '.js'
    std.saveFile(newfile, content)
    let init = map.get("__capturer__run_init" + options.id)
    if (!init) {//Ensure initialization only once
        map.put("__capturer__run_init" + options.id, options)
        bus.newWorker(options.id || '__capturer', newfile)
    }
}

/**
 * If the controller is a single thread, you can directly use the run function, which will automatically start a thread.
 * If you want to join another existing thread, you can use the following encapsulated function.
 */
capturer.worker = {
    //Before the while loop
    beforeLoop: function (options) {
        capturer.init(options, options.id)
        capturer.registerCallback(options.id)
    },
    //In a while loop
    loop: function (options) {
        if (!capturer.msgIsEmpty(options.id)) {
            let res = capturer.msgReceive(options.id);
            if (options.id === undefined || options.id === null || typeof options.id !== 'string') {
                // handle id
                options.id = ""
            }
        }
    }
}

export default capturer;
